#include <iostream>
#include <queue>
#include <vector>
#include <unistd.h>
#include "Mutex.hpp"
#include "Thread.hpp"
#include "Log.hpp"
#include "Cond.hpp"

namespace wxd
{
    const int default_thread_num = 5;
    template <class T>
    class ThreadPool
    {
    private:
        bool TaskIsEmpty()
        {
            return _task_queue.empty();
        }

        void EnableRunning()
        {
            _is_running = true;
        }
        void WakeAllThread()
        {
            if (_sleep_num > 0)
            {
                _cond.BroadCast();
            }
        }

        void WakeUpOne()
        {
            if (_sleep_num > 0)
            {
                _cond.Signal();
            }
        }

        void Join()
        {
            for (auto &thread : _threads)
            {
                thread->join();
                // LOG(LogLevel::DEBUG) << "等待线程:" << thread->GetName() << " 成功";
            }
        }

        // 构造函数，生成一批线程，私有化
        ThreadPool(int num = default_thread_num)
            : _num(num), _sleep_num(0), _is_running(false)
        {
            for (int i = 0; i < _num; i++)
            {
                _threads.emplace_back(new Thread([this]()
                                                 { TaskHandler(); }));
            }
        }

        // 启动线程池
        void Start()
        {
            if (_is_running)
                return;
            // 线程池状态设置启动
            EnableRunning();
            for (auto &thread : _threads)
            {
                thread->start();
            }
        }

    public:
        ThreadPool(const ThreadPool &thread_pool) = delete;
        ThreadPool &operator=(const ThreadPool &thread_pool) = delete;

        static ThreadPool<T> *GetInstance(int num = default_thread_num)
        {
            //要注意调用这个函数时，还没有实例化出对象，无法直接用成员变量的锁
            //所以要搞一个static锁
            if (_inc == nullptr)
            {
                LockGuard lock(&_s_mutex);
                if (_inc == nullptr)
                {
                    // 首次获取单例，创建
                    LOG(LogLevel::DEBUG) << "首次获取单例，创建";
                    _inc = new ThreadPool<T>(num);
                    _inc->Start();
                }
            }
            return _inc;
        }

        // 所有启动的线程池都会回调这个方法，要引入Mutex和Cond
        void TaskHandler()
        {

            char name[64];
            pthread_getname_np(pthread_self(), name, sizeof(name));
            while (true)
            {
                T task;
                {
                    LockGuard lock(&_mutex); // 加锁 RAII
                    LOG(LogLevel::DEBUG) << name << "尝试取出任务";
                    // 如果队列不为空，从队列中取任务，如果队列为空，转去休眠
                    // 只有线程池在运行同时任务为空才休眠
                    while (_is_running == true && TaskIsEmpty()) // 循环判断防止伪唤醒
                    {
                        // 去休眠
                        _sleep_num++;
                        LOG(LogLevel::DEBUG) << "没有任务 " << name << "去休眠";
                        _cond.Wait(_mutex);
                        _sleep_num--;
                        LOG(LogLevel::DEBUG) << name << "被唤醒";
                    }

                    // 在这里判断线程池是否停止了
                    if (_is_running == false && TaskIsEmpty())
                    {
                        LOG(LogLevel::DEBUG) << "线程池暂停&&没有任务，" << name << "退出";
                        break;
                    }

                    // 取任务
                    task = _task_queue.front();
                    _task_queue.pop(); // 此时任务已经归线程所有
                    // LOG(LogLevel::DEBUG) << name << "拿到任务";
                }
                // 执行任务
                LOG(LogLevel::DEBUG) << name << "开始执行任务";
                task(); // 回调任务方法
            }
        }

        // 用于外部给线程池中的线程发布任务
        void Enqueue(T task)
        {
            std::cout<<"2"<<std::endl;
            {
                LockGuard lock(&_mutex); // 加锁，防止多线程发布任务冲突
                // 如果线程池退出，不允许发布任务
                if (_is_running)
                {
                    // 任务不设置上限
                    _task_queue.push(task);
                    // 有休眠线程，唤醒一个
                    if (_sleep_num > 0)
                    {
                        LOG(LogLevel::DEBUG) << "尝试唤醒一个线程";
                        WakeUpOne();
                    }
                }
                else
                {
                    LOG(LogLevel::DEBUG) << "线程池已经退出，无法发布任务";
                }
            }
        }
        // 线程退出的设计
        void Stop()
        {
            // 线程可能在等待
            // 线程可能在执行任务
            // 线程可能刚执行完任务正要取任务
            // 如果队列中还有任务没处理，让线程处理完
            // 也就是只有当线程池状态not runnong && 队列为空 才可以退出
            {
                LockGuard lock(&_mutex);
                if (_is_running == true)
                {
                    _is_running = false;
                    // 唤醒所有被阻塞的线程
                    LOG(LogLevel::DEBUG) << "唤醒所有线程";
                    WakeAllThread();
                }
            }
            // 等待所有的线程
            Join();
        }

    private:
        std::queue<T> _task_queue;      // 存储任务的队列
        std::vector<Thread *> _threads; // 存放线程
        int _num;                       // 存放线程的个数
        Mutex _mutex;
        Cond _cond;
        int _sleep_num; // 记录休眠线程数
        bool _is_running;

        // 单例指针
        static ThreadPool<T> *_inc;
        //静态锁用于多线程同时互斥创建单例
        static Mutex _s_mutex;
    };

    // 初始化单例指针
    template <class T>
    ThreadPool<T> *ThreadPool<T>::_inc = nullptr;
    //初始化static锁
    template<class T>
    Mutex ThreadPool<T>::_s_mutex;
}
